﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Dotway.WPF.Controls.Utilities;

namespace Dotway.WPF.Controls
{
    [TemplatePart(Name = "PART_ControlContainer", Type = typeof(Panel))]
    [TemplatePart(Name = "PART_ClickControls", Type = typeof(Panel))]
    [TemplatePart(Name = "PART_RotateControls", Type = typeof(Panel))]
    [TemplatePart(Name = "PART_TranslateControls", Type = typeof(Panel))]
    [TemplatePart(Name = "PART_ScaleControls", Type = typeof(Panel))]
    public class TransformInteraction : ContentControl
    {
        #region Fields

        private Panel controlContainer = null;
        private TransformMode iteractionMode = TransformMode.None;
        private Point previousPosition = new Point();

        #endregion Fields
        #region Constructors

        public TransformInteraction()
        {
            this.MouseEnter += new MouseEventHandler(OnControlMouseEnter);
            this.MouseLeave += new MouseEventHandler(OnControlMouseLeave);
            this.MouseMove += new MouseEventHandler(OnControlMouseMove);
            this.MouseLeftButtonUp += new MouseButtonEventHandler(OnControlMouseLeftButtonUp);
        }

        static TransformInteraction()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TransformInteraction), new FrameworkPropertyMetadata(typeof(TransformInteraction)));
        }

        #endregion Contructors
        #region Events

        public event EventHandler<EventArgs> Click;
        private void OnClick()
        {
            if (Click != null)
            {
                Click(this, EventArgs.Empty);
            }
        }

        #endregion Events
        #region Properties

        public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(double), typeof(TransformInteraction), new PropertyMetadata(1.0));
        public double Scale
        {
            get { return (double)GetValue(ScaleProperty); }
            set { SetValue(ScaleProperty, value); }
        }


        public static readonly DependencyProperty AngleProperty = DependencyProperty.Register("Angle", typeof(double), typeof(TransformInteraction), new PropertyMetadata(0.0));
        public double Angle
        {
            get { return (double)GetValue(AngleProperty); }
            set { SetValue(AngleProperty, value); }
        }


        public static readonly DependencyProperty XProperty = DependencyProperty.Register("X", typeof(double), typeof(TransformInteraction), new PropertyMetadata(0.0));
        public double X
        {
            get { return (double)GetValue(XProperty); }
            set { SetValue(XProperty, value); }
        }


        public static readonly DependencyProperty YProperty = DependencyProperty.Register("Y", typeof(double), typeof(TransformInteraction), new PropertyMetadata(0.0));
        public double Y
        {
            get { return (double)GetValue(YProperty); }
            set { SetValue(YProperty, value); }
        }


        #endregion Properties
        #region Methods

        private double GetScale(Point currentPosition)
        {
            double halfWidth = ActualWidth / 2.0;
            double halfHeight = ActualHeight / 2.0;
            double x = (currentPosition.X - (X + halfWidth)) / halfWidth;
            double y = (currentPosition.Y - (Y + halfHeight)) / halfHeight;

            double newScale = Math.Sqrt(Math.Pow(x, 2.0) + Math.Pow(y, 2.0));
            return newScale;
        }

       // Pygmy contributed with a bug fix 2010 09 03.
       private double GetDeltaAngle(Point currentPosition)
       {
           double halfWidth = ActualWidth / 2.0;
           double halfHeight = ActualHeight / 2.0;

           Point previousPosition = new Point(this.previousPosition.X - (X + halfWidth), this.previousPosition.Y - (Y+halfHeight));

           currentPosition.X -= X + halfWidth;
           currentPosition.Y -= Y + halfHeight;

           double previousAngle = Math.Atan2(previousPosition.Y, previousPosition.X);
           double currentAngle = Math.Atan2(currentPosition.Y, currentPosition.X);

           // Return the delta angle and convert from radians to degrees (180/pi = 57.2957...).
           return (currentAngle - previousAngle) * 57.2957;
       }

        private void Rotate(double deltaAngle)
        {
            Angle += deltaAngle;
        }

        private void Translate(double deltaX, double deltaY)
        {
            X += deltaX;
            Y += deltaY;
        }

        private void ScaleXY(double newScale)
        {
            Scale = newScale;
        }

        public override void OnApplyTemplate()
        {
            Panel clickControls = this.GetTemplateChild("PART_ClickControls") as Panel;
            if (clickControls == null)
            {
                throw new NullReferenceException("Could not find templated part: PART_ClickControls.");
            }
            clickControls.MouseLeftButtonDown += new MouseButtonEventHandler(OnClickControlsMouseLeftButtonDown);

            Panel rotateControls = this.GetTemplateChild("PART_RotateControls") as Panel;
            if (rotateControls == null)
            {
                throw new NullReferenceException("Could not find templated part: PART_RotateControls.");
            }
            rotateControls.MouseLeftButtonDown += new MouseButtonEventHandler(OnRotateControlsMouseLeftButtonDown);

            Panel translateControls = this.GetTemplateChild("PART_TranslateControls") as Panel;
            if (translateControls == null)
            {
                throw new NullReferenceException("Could not find templated part: PART_TranslateControls.");
            }
            translateControls.MouseLeftButtonDown += new MouseButtonEventHandler(OnTranslateControlsMouseLeftButtonDown);

            Panel scaleControls = this.GetTemplateChild("PART_ScaleControls") as Panel;
            if (scaleControls == null)
            {
                throw new NullReferenceException("Could not find templated part: PART_ScaleControls.");
            }
            scaleControls.MouseLeftButtonDown += new MouseButtonEventHandler(OnScaleControlsMouseLeftButtonDown);

            controlContainer = this.GetTemplateChild("PART_ControlContainer") as Panel;
            if (controlContainer == null)
            {
                throw new NullReferenceException("Could not find templated part: PART_ControlContainer.");
            }

            base.OnApplyTemplate();
        }

        private void OnControlMouseMove(object sender, MouseEventArgs e)
        {
            if (iteractionMode != TransformMode.None)
            {
                Point currentPosition = e.GetPosition(this);

                if (iteractionMode == TransformMode.Translating)
                {
                    Translate(currentPosition.X - previousPosition.X, currentPosition.Y - previousPosition.Y);
                }
                else if (iteractionMode == TransformMode.Rotating)
                {
                    double deltaAngle = GetDeltaAngle(currentPosition);
                    Rotate(deltaAngle);
                }
                else if (iteractionMode == TransformMode.Scaling)
                {
                    double newScale = GetScale(currentPosition);
                    ScaleXY(newScale);
                }

                previousPosition = currentPosition;
            }
        }

        private void OnTranslateControlsMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            previousPosition = e.GetPosition(this);
            this.CaptureMouse();
            iteractionMode = TransformMode.Translating;
        }

        private void OnRotateControlsMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            previousPosition = e.GetPosition(this);
            this.CaptureMouse();
            iteractionMode = TransformMode.Rotating;
        }

        private void OnScaleControlsMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            previousPosition = e.GetPosition(this);
            this.CaptureMouse();
            iteractionMode = TransformMode.Scaling;
        }

        private void OnClickControlsMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            OnClick();
        }

        private void OnControlMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            iteractionMode = TransformMode.None;
            Mouse.Capture(null);
        }

        private void OnControlMouseLeave(object sender, MouseEventArgs e)
        {
            controlContainer.Visibility = Visibility.Hidden;
        }

        private void OnControlMouseEnter(object sender, MouseEventArgs e)
        {
            controlContainer.Visibility = Visibility.Visible;
        }

        #endregion Methods
    }
}
